package helper;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;

import java.sql.SQLException;

import model.Ebene;
import model.EbenenRepository;
import model.Feld;

/**
 * Klasse zur Latex-Kompilierung der Modulhandbücher.
 * 
 * @author Lukas Egger, Jonas Gutermuth
 */

public class Export {

	private static String latex = "";
	private static String cachetest1 = "";
	private static String cachetest2 = "";
	private static Ebene startEbene;
	private static int ebenenLevel;
	private static int tabellenZeile;
	private static int tabellenAnzahl;
	private static boolean neusteVersion;
	private static boolean dateiGeneriert;
	static EbenenRepository ebenenRepository;

	/**
	 * Aufruf der pdf-Generierung für die neueste Version einer Ebene.
	 * 
	 * @param id
	 *            die Ebenen-ID
	 * @throws ClassNotFoundException
	 * @throws SQLException
	 * @throws IOException
	 */
	public static boolean generiereAktuellsteVersion(int id, long erstellungszeit) throws ClassNotFoundException, SQLException, IOException {
		neusteVersion = true;
		ebenenRepository = EbenenRepository.getInstance();
		return compilePDF(id, erstellungszeit);
	}

	/**
	 * Aufruf der pdf-Generierung für eine Version einer Ebene mit bestimmtem
	 * Datum.
	 * 
	 * @param id
	 *            die Ebenen-ID
	 * @param erstellungszeit
	 *            die Erstellungszeit für die Ebenenversion
	 * @throws ClassNotFoundException
	 * @throws SQLException
	 * @throws IOException
	 */
	public static boolean generiereVersionierteVersion(int id, long erstellungszeit) throws ClassNotFoundException, SQLException, IOException {
		neusteVersion = false;
		ebenenRepository = EbenenRepository.getInstance();
		return compilePDF(id, erstellungszeit);
	}

	/**
	 * Generierung einer pdf-Datei anhand der Ebenen-ID und der Erstellungszeit
	 * der entsprechenden Ebenenversion.
	 * 
	 * @param ebenenId
	 * @param erstellungszeit
	 * @throws ClassNotFoundException
	 * @throws SQLException
	 * @throws IOException
	 */
	private static boolean compilePDF(int ebenenId, long erstellungszeit) throws ClassNotFoundException, SQLException, IOException {
		latex = "";
		cachetest1 = "";
		cachetest2 = "";
		ebenenLevel = 1;
		dateiGeneriert = true;
		tabellenAnzahl = 0;
		
		//entweder neuste Ebenenversion oder aktuellste vor Stichtag laden
		if(neusteVersion){
			startEbene = ebenenRepository.getNeusteEbeneNachId(ebenenId);
		}else{
			startEbene = ebeneVorStichtag(ebenenId, erstellungszeit);
		}
		
		
		//latex-header laden
		FileReader header = new FileReader("template/texheader.template");
		BufferedReader headerreader = new BufferedReader(header);
		String temp;
		do {
			temp = headerreader.readLine();
			if(temp != null) {
				latex += temp;
			}
			
		} while(temp != null);
		headerreader.close();
		

		//header vervollständigen
		latex += "\\title{" + startEbene.getName() + "} " + "\\date{ } "
			   + "\\begin{document} "
			   + "\\author{Universität-Ulm} "
			   + "\\maketitle "
			   + "\\definecolor{hellgrau}{rgb}{0.90,0.90,0.90} ";
		
		//falls keine Unterlemente, kein Inhaltsverzeichnis anlegen
		if(startEbene.getUnterelement() != null){
			latex +=  "\\tableofcontents ";
		}
			   
		//rekursiv Unterebenen und Felder anlegen
		latex += naechsteEbene(startEbene, ebenenId, erstellungszeit);
		latex = latex + "\\end{document}";
		
		// problematische Sonderzeichen ersetzen
		latex = convertLatex(latex);
		
		//latexString fertig zusammengestellt
		
		//falls tex-Datei bereits existiert
		java.io.File file = new java.io.File("" + ebenenId + "_" + erstellungszeit + ".tex");
		if(file.exists()){
			//latex-String in txt-Datei speichern für Vergleich
			FileOutputStream cacheDatei;
			File cache;
			cache = new File("" + ebenenId + "_" + erstellungszeit + ".txt");
			cache.createNewFile();
			cacheDatei = new FileOutputStream("" + ebenenId + "_" + erstellungszeit +".txt");
			Writer out2 = new BufferedWriter(new OutputStreamWriter(cacheDatei, "UTF-8"));
			out2.write(latex);
			out2.close();
			
			//latexstring aus bereits bestehender tex-Datei laden
			try {
				BufferedReader in = new BufferedReader(new FileReader("" + ebenenId + "_" + erstellungszeit + ".tex"));
				String zeile = null;
				while ((zeile = in.readLine()) != null) {
					cachetest1 += zeile;
				}
				in.close();
			} catch (IOException e) {
				new helper.Logging("Fehler Fxxx",e.toString(), null, (byte)1);
				//TODO
				e.printStackTrace();
			}
			//latexstring aus txt-Datei laden
			try {
				BufferedReader in = new BufferedReader(new FileReader("" + ebenenId + "_" + erstellungszeit + ".txt"));
				String zeile = null;
				while ((zeile = in.readLine()) != null) {
					cachetest2 += zeile;
				}
				in.close();
			} catch (IOException e) {
				//TODO
				new helper.Logging("Fehler Fxxx",e.toString(), null, (byte)1);
				e.printStackTrace();
			}
			//Vergleich der beiden Strings
			if(cachetest1.equals(cachetest2)){
				System.out.println("caching erfolgreich...");
				return dateiGeneriert;
			}else{
				System.out.println("caching nicht erfolgreich...");
			}
		}

		//Dateien erstellen
		FileOutputStream texDatei;
		File tex;

		tex = new File("" + ebenenId + "_" + erstellungszeit + ".tex");
		tex.createNewFile();
		
		texDatei = new FileOutputStream("" + ebenenId + "_" + erstellungszeit + ".tex");
		Writer out = new BufferedWriter(new OutputStreamWriter(texDatei, "UTF-8"));
		out.write(latex);
		out.close();

		//je nach Betriebssystem anderen Befehl ausführen
		String latexpfad = "pdflatex";
		if (System.getProperty("os.name").startsWith("Mac")) {
			latexpfad = "/usr/texbin/pdflatex";
		}
		Process p = Runtime.getRuntime().exec(
				latexpfad + " -synctex=1 -interaction=nonstopmode "
						+ tex.getAbsolutePath());

		BufferedReader in = new BufferedReader(new InputStreamReader(
				p.getInputStream()));
		String line;
		while ((line = in.readLine()) != null) {
			//Falls keine Outputfile generiert wird, abfangen
			if(line.indexOf("no output PDF file", 0) != -1){
				dateiGeneriert = false;
				break;
			}
		}
		//falls keine Datei generiert, return
		System.out.println(tex.getAbsolutePath());
		if(!dateiGeneriert){
			return dateiGeneriert;
		}
		in.close();
		//zweites kompilieren um Inhaltsverzeichnis zu erzeugen
		p = Runtime.getRuntime().exec(
				latexpfad + " -synctex=1 -interaction=nonstopmode "
						+ tex.getAbsolutePath());
		in = new BufferedReader(new InputStreamReader(p.getInputStream()));
		while ((line = in.readLine()) != null) {
			//System.out.println(line);
		}
		in.close();
		
		return dateiGeneriert;
		
	}

	/**
	 * Gibt die letzte Version einer Ebene vor einem Stichtag zurück.
	 * 
	 * @param ebenenId
	 *            die ID der Ebene
	 * @param erstellungszeit
	 *            der Stichtag, vor dem eine Ebenenversion gefunden werden will
	 * @return die letzte Version der Ebene mit der dem Erstelldatum vor dem
	 *         Stichtag
	 */
	private static Ebene ebeneVorStichtag(int ebenenId, long erstellungszeit) {
		//alle Ebenen nach Id laden
		Ebene[] roots = ebenenRepository.getEbeneNachId(ebenenId);
		int min = 0;
		Ebene tmp;
		//älteste Ebene ermitteln
		for(int i=0; i< roots.length-1; i++) {
			min = i;
			for(int j=i+1; j<roots.length; j++) {
				if(roots[j].getErstellungszeit() < roots[min].getErstellungszeit()) {
					min = j;
				}
			}
			tmp = roots[i];
			roots[i] = roots[min];
			roots[min] = tmp;
		}
		//Ebene ermitteln, deren Erstellungszeit am nächsten an der übergebenen Zeit liegt
		Ebene ebeneVorStichtag = roots[min];
		if (roots[0].getErstellungszeit() <= erstellungszeit)
			ebeneVorStichtag = roots[0];
		for (int m = 0; m < roots.length - 1; m++) {
			if (roots[m + 1].getErstellungszeit() <= erstellungszeit
					&& roots[m].getErstellungszeit() < roots[m + 1]
							.getErstellungszeit()) {
				ebeneVorStichtag = roots[m + 1];
			}
		}
		return ebeneVorStichtag;
	}

	private static String felderEinfuegen(Ebene ebene) throws ClassNotFoundException, SQLException{
		//Mögliche Felder einfügen
		String output = "";
		Feld[] tmp1;
		tmp1 = ebene.getFeld();

		//extra .tex-Datei erstellen für longtable
		output += "\\begin{filecontents}{Tabelle"+ebene.getId()+ebene.getErstellungszeit()+tabellenAnzahl+".tex} \r\n ";
		output += "\\begin{longtable}{R{4.5cm}X} \r\n ";
			
		tabellenZeile = 0;
		for (int l = 0; l < tmp1.length; l++) {
				// jede zweite Zeile einfärben
				if (tabellenZeile % 2 == 0) {
					output += "\\rowcolor{hellgrau} \r\n ";
				}
				output += tmp1[l].getName() + " & "
						+ tmp1[l].getWert() + "  \\\\ \r\n ";
				tabellenZeile++;
			
		}
		//Tabelle schließen
		output += "\\end{longtable} \r\n \\end{filecontents} \r\n \\LTXtable{17cm}{Tabelle"+ebene.getId()+ebene.getErstellungszeit()+tabellenAnzahl+".tex} \r\n ";
		tabellenAnzahl++;
		return output;
	}
	
	
	/**
	 * Gibt den Inhalt der nächsten Unterebene zurück.
	 * 
	 * @param ebene
	 *            die Ebene
	 * @param ebenenId
	 *            die Ebenen-ID
	 * @param erstellungszeit
	 *            die Erstellungszeit der Ebene
	 * @return LaTex-Code für die Erstellung der pdf-Datei
	 * @throws ClassNotFoundException
	 * @throws SQLException
	 */
	private static String naechsteEbene(Ebene ebene, int ebenenId,
			long erstellungszeit) throws ClassNotFoundException, SQLException {
		String output = "";
		
		//falls Prosa vorhanden einfügen
		if(ebene.getProsa() != null){
			output += ebene.getProsa().getWert()+"\\newline \\newline \r\n ";
		}
		
		//falls Felder vorhanden einfügen
		if(ebene.getFeld() != null){
			output += felderEinfuegen(ebene);
			output += " \\newpage \r\n ";
		}
		//Mögliche Unterebenen einfügen
		Ebene[] tmp2;
		tmp2 = ebene.getUnterelement();

		if (tmp2 != null) {
			for (int j = 0; j < tmp2.length; j++) {
				System.out.println("Ebene:"+tmp2[j].getId());
				if (tmp2[j] != null) {
					//je nach Level, entsprechenden Latexbefehl für Überschrift verwenden
					switch (ebenenLevel) {
					case 1:
						output += " \\newpage ";	
						output += " \\section{" + ebeneVorStichtag(tmp2[j].getId(), erstellungszeit).getName() + "} \r\n ";
						break;
					case 2:
						output += " \\subsection{" + ebeneVorStichtag(tmp2[j].getId(), erstellungszeit).getName() + "} \r\n ";
						break;
	
					default:
						output += " \\subsubsection{" + ebeneVorStichtag(tmp2[j].getId(), erstellungszeit).getName() + "} \r\n ";
						break;
					}
	
					ebenenLevel++;
					output += naechsteEbene(ebeneVorStichtag(tmp2[j].getId(), erstellungszeit), ebenenId, erstellungszeit);

					//wenn alle Unterelemente bearbeitet, Ebenenlevel wieder senken
					if(j == tmp2.length-1){
						ebenenLevel--;
						output += " \\newpage \r\n ";
					}					
				}
			}
		}else{ //falls keine Unterelemente vorhanden, Ebenenlevel senken
			ebenenLevel--;
		}
		return output;
	}

	/**
	 * Ersetzt alle in Latex problematischen Sonderzeichen um Fehler zu vermeiden.
	 * 
	 * @param string
	 *            eventuell fehlerhafter String
	 * @return korrigierten LaTex-Code für die Erstellung der pdf-Datei
	 */
	public static String convertLatex(String string) {
		StringBuffer sb = new StringBuffer(string.length());
		int len = string.length();
		char c;
		for (int i = 0; i < len; i++) {
	        c = string.charAt(i);
        	if (c == '_') {
        		sb.append("\\_");
        	} else if (c == '"') {
                sb.append("\"'");
        	} else if (c == '%') {
                sb.append("\\%");
        	} else if (c == '$') {
                sb.append("\\textdollar");
        	} else if (c == '§') {
                sb.append("\\S");
        	}  else if (c == '+') {
                sb.append("\\plus");
        	}  else {
        		sb.append(c);
            }
        }
	    return sb.toString();
	}	
	/**
	 * Ersetzt alle in Latex problematischen Sonderzeichen um Fehler zu vermeiden.
	 * 
	 * @param string
	 *            eventuell fehlerhafter String
	 * @return korrigierten LaTex-Code für die Erstellung der pdf-Datei
	 */
	public static String convertHtml(String string) {
		
		string = string.replace("\\_", "_");
		string = string.replace("\\_", "_");
		string = string.replace("\"'", "\\\"");
		string = string.replace("\\%", "%");
		string = string.replace("\\textdollar", "$");
		string = string.replace("\\plus", "+");
	    return string;
	}

}
